/**
	Pulsar engine. Core.
	Additional include file. Memory monitor and statistics.

	Copyright (C) 2013-2014 Dmitry Dolgov

	This program is free software; you can redistribute it and/or modify it under the terms of the
	GNU General Public License as published by the Free Software Foundation; either version 2 of
	the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
	without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	See the GNU General Public License for more details.
*/

namespace pl
{

const pl_uint32 SMALL_BLOCK_SIZE_MAX = 32;

struct MemoryMonitorHdr
{
	pl_uint16 eventCode : 5;        // enum BlockType
	pl_uint16 deleteFlag : 1;       // true if delete was called
	pl_uint16 pointerPresent : 2;   // 00 - none , 01 - specified, 10 - the same as prev struct, 11 - reserved
	pl_uint16 sizePresent : 2;      // 00 - none , 01 - specified, 10 - the same as prev struct, 11 - reserved
	pl_uint16 fileNamePresent : 2;  // 00 - none , 01 - specified, 10 - the same as prev struct, 11 - reserved
	pl_uint16 lineNmbPresent : 2;   // 00 - none , 01 - specified, 10 - the same as prev struct, 11 - reserved
	pl_uint16 textPresent : 1;      // ASCIIZ string presents
	pl_uint16 pad : 1;              // Reserved
};

/**
	Memory monitor events
	Can be used to create memory monitor and allocation traps
*/
#define DECLARE_ON_ALLOC()        virtual void onAlloc(void* p, size_t size, const pl_char* file, pl_uint32 line, const pl_char* func, BlockType type)
#define DECLARE_ON_FREE()         virtual void onFree(void* p, size_t size, const pl_char* file, pl_uint32 line, const pl_char* func, BlockType type)

class IMemoryMonitor : public pl::ListNode<IMemoryMonitor>
{
public:
	virtual ~IMemoryMonitor() {}

	static void _callOnAlloc(void* p, size_t size, const pl_char* file, pl_uint32 line, const pl_char* func, BlockType type);
	static void _callOnFree(void* p, size_t size, const pl_char* file, pl_uint32 line, const pl_char* func, BlockType type);

protected:
	explicit IMemoryMonitor();

	DECLARE_ON_ALLOC() {}
	DECLARE_ON_FREE() {}

private:
	static pl::List<IMemoryMonitor> _memMonitorGroup;
};

/**
	Standard memory monitor subscribers: file tracer
*/
class MemoryMonitorFile : public IMemoryMonitor
{
public:
	explicit MemoryMonitorFile(const pl::Filepath& monitorFile);
	virtual ~MemoryMonitorFile();

	DECLARE_ON_ALLOC();
	DECLARE_ON_FREE();

	void pushEvent(const pl_char* evt);

private:
	void _saveInfo(MemoryMonitorHdr& hdr, void* p, size_t size, const pl_char* file, pl_uint32 line, const pl_char* func);

	template <class T> void _write(const T& v)
	{
		_write(&v, sizeof(T));
		return;
	}
	void _write(const void* pBuf, pl_int32 itemSize);
	void _writeStringAnsi(const pl_char* str);
	void _flush();

	pl::StreamWrite* _pStream;

	pl_uint8 _cacheArea[4096];
	pl_uint32 _curPtr;
	pl::CriticalSection _memCs;

	void* _prevPointerInfo;
	pl_uint32 _prevSizeInfo;
	pl::Filepath _prevFileName;
	pl_uint32 _prevLineNum;
};

struct MemoryStat
{
	/// Number of currently existing allocations
	pl_uint32 nAllocCurrentBlocks;
	/// Total number of allocations
	pl_uint32 nAllocSumBlocks;
	/// Total number of free blocks
	pl_uint32 nFreeBlocks;
	/// Amount of current allocation size
	pl_uint64 nAllocSize;
	/// Amount of current allocation size
	pl_uint64 nSumAllocSize;
	/// Minimal pointer at allocation
	void* allocatedPtrMin;
	/// Maximal pointer at allocation
	void* allocatedPtrMax;
	/// Number of small allocated blocks
	pl_uint32 nSmallAllocations[SMALL_BLOCK_SIZE_MAX];
	// Peak allocation information
	/// Peak allocation: max block size
	pl_uint64 peakAllocationSize;
	/// Peak allocation: block index
	pl_uint32 peakAllocationSizeIndex;
	/// Peak allocation: max number of blocks
	pl_uint32 peakAllocationCount;
	/// Peak allocation: max number of blocks (block index)
	pl_uint32 peakAllocationCountIndex;
};


/**
	Standard memory monitor subscribers: allocation statistics
*/
class MemoryMonitorStat : public IMemoryMonitor
{
public:
	explicit MemoryMonitorStat();
	virtual ~MemoryMonitorStat() {}

	inline void getStat(MemoryStat& stat)
	{
		stat = _stat;
	}

private:
	DECLARE_ON_ALLOC();
	DECLARE_ON_FREE();

	void _adjustMinMax(void* p);
	void _adjustPeaks();

	MemoryStat _stat;
	pl::CriticalSection _cs;
};

} // namespace pl

//
// End of file 'pulsar_memorymonitor.h'
//
